package com.qen.encrypt.utils;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.KeyUtil;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.symmetric.SM4;
import com.qen.encrypt.properties.AlgorithmEnum;
import lombok.SneakyThrows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;

/**
 * SM2签名
 *
 * @author licz
 * @data 2025年05月27日 3:52 PM
 */
public class SM4Helper implements ParamEncrypt {
    private final Logger log = LoggerFactory.getLogger(getClass());

    //    private SM4 sm4;
    private Mode mode;
    private Padding padding;
    private String key;

//    public SM4Helper() {
//        this.sm4 = SmUtil.sm4();
//    }

    public SM4Helper(AlgorithmEnum algorithm, String key) {
        Mode mode = algorithm.getMode();
        Padding padding = algorithm.getPadding();
//        SecretKey secretKey = KeyUtil.generateKey(SM4.ALGORITHM_NAME, key.getBytes(StandardCharsets.UTF_8));
//        this.sm4 = new SM4(mode, padding, secretKey);
        this.mode = mode;
        this.key = key;
        this.padding = padding;
    }

    @SneakyThrows
    private byte[] initIv() {
        SecureRandom sr = SecureRandom.getInstanceStrong();
        //生成16个字节的随机数
        return sr.generateSeed(16);
    }

    private byte[] join(byte[] bs1, byte[] bs2) {
        byte[] r = new byte[bs1.length + bs2.length];
        System.arraycopy(bs1, 0, r, 0, bs1.length);
        System.arraycopy(bs2, 0, r, bs1.length, bs2.length);
        return r;
    }

    @Override
    public String pubEncrypt(byte[] data) {
        byte[] iv = initIv();
        SecretKey secretKey = KeyUtil.generateKey(SM4.ALGORITHM_NAME, key.getBytes(StandardCharsets.UTF_8));
        SM4 sm4 = new SM4(mode, padding, secretKey);
        if (mode.equals(Mode.CTR)) {
            sm4.setIv(iv);
            byte[] en = sm4.encrypt(data);
            byte[] out = join(iv, en);
            return Base64.encode(out);
        } else {
            return sm4.encryptBase64(data);
        }
    }

    @Override
    public String pubEncrypt(String data) {
        return pubEncrypt(StrUtil.bytes(data, CharsetUtil.CHARSET_UTF_8));
    }

    @Override
    public byte[] priDecrypt(byte[] input) {
        SecretKey secretKey = KeyUtil.generateKey(SM4.ALGORITHM_NAME, key.getBytes(StandardCharsets.UTF_8));
        SM4 sm4 = new SM4(mode, padding, secretKey);
        if (mode.equals(Mode.CTR)) {
            byte[] iv = new byte[16];
            byte[] data = new byte[input.length - 16];
            //iv
            System.arraycopy(input, 0, iv, 0, 16);
            //data
            System.arraycopy(input, 16, data, 0, data.length);
            sm4.setIv(iv);
            return sm4.decrypt(data);
        } else {
            return sm4.decrypt(input);
        }
    }

    @Override
    public String priDecrypt(String data) {
        return new String(priDecrypt(Base64.decode(data)), StandardCharsets.UTF_8);
    }

    @Override
    public String priEncrypt(byte[] data) {
        return pubEncrypt(data);
    }

    @Override
    public String priEncrypt(String data) {
        return pubEncrypt(data);
    }

    @Override
    public byte[] pubDecrypt(byte[] data) {
        return priDecrypt(data);
    }

    @Override
    public String pubDecrypt(String data) {
        return priDecrypt(data);
    }

}
